<template>
  <view class="fu-calendar-wrap">
    <!-- 日历选择 start -->
    <view class="fu-calendar-data fu-flex fu-justify-center fu-align-center fu-margin fu-height-72" v-if="changeTitle">
      <block v-if="changeYear">
        <view class="fu-arrow fu-left fu-flex fu-justify-between fu-align-center" @click="prevYear">
          <text class="iconfont icon-changyongicon-" :style="[yaColor]"></text>
        </view>
      </block>
      <block v-if="changeMonth">
        <view class="fu-arrow single fu-left fu-flex fu-justify-between fu-align-center" @click="prevMonth">
          <text class="iconfont icon-jiantouyou" :style="[maColor]"></text>
        </view>
      </block>
      <view class="fu-title fu-text-32 fu-text-333">{{ currentDate[0] }}{{ $t('年') }}{{ currentDate[1] }}{{ $t('月') }}
      </view>
      <block v-if="changeMonth">
        <view class="fu-arrow single fu-right fu-flex fu-justify-between fu-align-center" @click="nextMonth">
          <text class="iconfont icon-jiantouyou" :style="[maColor]"></text>
        </view>
      </block>
      <block v-if="changeYear">
        <view class="fu-arrow fu-flex fu-right fu-justify-between fu-align-center" @click="nextYear">
          <text class="iconfont icon-changyongicon-" :style="[yaColor]"></text>
        </view>
      </block>
    </view>
    <!-- 日历选择 end -->
    <!-- 星期 start -->
    <view class="fu-week fu-flex fu-justify-between fu-align-center fu-text-333 fu-height-72">
      <view class="fu-week-item fu-text-center" v-for="item in weekData" :key="item">{{ item }}</view>
    </view>
    <!-- 星期 end -->
    <!-- 日历 start -->
    <view class="fu-calendar fu-flex fu-justify-between fu-align-center fu-flex-wrap fu-text-333">
      <!-- mode == date || mode == range -->
      <view class="fu-calendar-item fu-text-center" @click="changeCalendar(item, index)"
        :class="{ isNot: item.isNot, isFuture: item.isFuture && !item.isNot }" v-for="(item, index) in calendar"
        :key="index">
        <view class="fu-calendar-num fu-flex fu-justify-center fu-align-center"
          :class="{ signin: mode == 'signin', range: mode == 'range', start: item.start, end: item.end }"
          :style="[(item.dayActive || item.start || item.end) && !item.isNot ? dayBg : {}, item.active && !item.isNot ? activeBg : {}]">
          {{ item.day }}
          <view class="fu-calendar-text" v-show="item.start">{{ startText }}</view>
          <view class="fu-calendar-text" v-show="item.end">{{ endText }}</view>
        </view>
      </view>
    </view>
    <!-- 日历 end -->
  </view>
</template>

<script>
  /**
   * @author 邓东方
   */
  export default {
    props: {
      // 月份切换按钮箭头颜色
      'month-arrow-color': {
        type: String,
        default: '#666666',
      },
      // 年份切换按钮箭头颜色
      'year-arrow-color': {
        type: String,
        default: '#999999',
      },
      // 是否显示顶部年月
      'change-title': {
        type: Boolean,
        default: true,
      },
      // 是否显示顶部的切换年份方向的按钮
      'change-year': {
        type: Boolean,
        default: true,
      },
      // 是否显示顶部的切换月份方向的按钮
      'change-month': {
        type: Boolean,
        default: true,
      },
      // 日期模式 date 单个日期选择模式  range 日期段选择模式  signin 签到日期展示模式
      mode: {
        type: String,
        default: 'date',
      },
      // 未来日期是否可选择 默认不可选择
      future: {
        type: Boolean,
        default: false,
      },
      // 日期段回显时 若只传入一个日期则不显示
      //日期段选择时默认显示开始时间
      'start-time': {
        type: String,
        default: '',
      },
      //日期段选择时默认显示结束时间
      'end-time': {
        type: String,
        default: '',
      },
      // 起始日期底部的提示文字
      'start-text': {
        type: String,
        default: global.i18n.t('开始'),
      },
      // 结束日期底部的提示文字
      'end-text': {
        type: String,
        default: global.i18n.t('结束'),
      },
      // 选择日期开始结束当天背景色
      'active-bg-color': {
        type: String,
        default: 'rgba(41,121,255,1)',
      },
      // 选择日期中间的背景色
      'range-bg-color': {
        type: String,
        default: 'rgba(41,121,255,0.13)',
      },
      // 当前日期是否高亮
      isDefaultDay: {
        type: Boolean,
        default: true,
      },
      // 单个日期选择默认选中时间 xxxx-yy-dd
      currenTime: {
        type: String,
        default: '',
      },
      // 签到模式数据 [xxxx-yy-dd]
      signinData: {
        type: Array,
        default: function() {
          return []
        },
      },
    },
    data() {
      return {
        weekData: [
          global.i18n.t('z日'),
          global.i18n.t('z一'),
          global.i18n.t('z二'),
          global.i18n.t('z三'),
          global.i18n.t('z四'),
          global.i18n.t('z五'),
          global.i18n.t('z六'),
        ], //星期
        calendar: [], //日历数组
        currentDate: [], //当前日期
        currentDay: '', //当天时间
        start: '', //开始时间
        end: '', //结束时间
        isEmit: true, //mode == range是否推送
      }
    },
    computed: {
      // 背景色
      dayBg() {
        return {
          background: this.activeBgColor,
          color: '#ffffff'
        }
      },
      // 时间段背景色
      activeBg() {
        return {
          background: this.rangeBgColor,
          color: '#ffffff'
        }
      },
      // 年份箭头颜色
      yaColor() {
        return {
          color: this.yearArrowColor
        }
      },
      // 月份箭头颜色
      maColor() {
        return {
          color: this.monthArrowColor
        }
      },
    },
    watch: {
      signinData: {
        deep: true,
        handler(newVal, oldVal) {
          if (this.mode == 'signin') {
            this.singinFun(this.calendar, newVal)
          }
        },
      },
    },
    mounted() {
      let currentDate = this.currentime()
      let y = currentDate[0]
      let m = currentDate[1] > 9 ? currentDate[1] : '0' + currentDate[1]
      let d = currentDate[2] > 9 ? currentDate[2] : '0' + currentDate[2]
      this.currentDate = currentDate
      //  mode == 'date' 在传入单个时间时 currentDay不再显示默认的当前时间
      if (this.mode == 'date') {
        if (this.currenTime) {
          this.currentDay = this.currenTime
        } else {
          if (this.isDefaultDay) {
            this.currentDay = `${y}-${m}-${d}`
          }
        }
      }
      //  mode == 'date' isDefaultDay == true时 当前时间才显示
      if (this.mode == 'range') {
        if (this.isDefaultDay) {
          this.currentDay = `${y}-${m}-${d}`
        }
      }
      // 日历赋值
      this.calendar = this.getCalendar(currentDate[0], currentDate[1], currentDate[2])
    },
    methods: {
      /**
       * @description 获取当前时间函数
       */
      currentime() {
        var date = new Date()
        var y = Number(date.getFullYear())
        var m = Number(date.getMonth() + 1)
        var d = Number(date.getDate())
        return [y, m, d]
      },
      /**
       * @description 获取日历
       * @param {String,Number} y 年
       * @param {String,Number} m 月
       */
      getCalendar(y, m) {
        // 求解cy年cm月cd日是星期几,parseInt代表取整 d=1是去每个月第一天
        var cc = parseInt(y / 100) //c
        var cy = y - cc * 100 //y
        var cm = m //m
        var cd = 1 //d
        // 某年的1、2月要看作上一年的13、14月来计算，比如2003年1月1日要看作2002年的13月1日来计算
        if (m == 1 || m == 2) {
          cc = parseInt((y - 1) / 100)
          cy = y - 1 - cc * 100
          cm = 12 + m
        }
        //w=y+[y/4]+[c/4]-2c+[26(m+1）/10]+d-1
        // var csum = y + [y / 4] + [c / 4] - 2c+[26(m + 1）/10]+d-1;
        var csum = cy + parseInt(cy / 4) + parseInt(cc / 4) - 2 * cc + parseInt((26 * (cm + 1)) / 10) + cd - 1
        // 注意使用蔡勒公式时出现负数情况    fd 每月第一天星期几
        if (csum < 0) {
          var fd = parseInt(((csum % 7) + 7) % 7)
        } else {
          var fd = parseInt(csum % 7)
        }

        // 上个月天数
        var cond1 = y % 4 == 0 //条件1：年份必须要能被4整除
        var cond2 = y % 100 != 0 //条件2：年份不能是整百数
        var cond3 = y % 400 == 0 //条件3：年份是400的倍数
        //当条件1和条件2同时成立时，就肯定是闰年，所以条件1和条件2之间为“与”的关系。
        //如果条件1和条件2不能同时成立，但如果条件3能成立，则仍然是闰年。所以条件3与前2项为“或”的关系。
        //所以得出判断闰年的表达式：
        var cond = (cond1 && cond2) || cond3
        //判断当月有多少天
        var allDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][m - 1]
        if (cond && m == 2) {
          allDays = 29
        }
        //上个月是不是去年
        let prevYear = y
        let prevMonth = m - 1
        if (m == 1) {
          prevYear = y - 1
          prevMonth = 12
        }
        let _prevMonth = prevMonth > 9 ? prevMonth : '0' + prevMonth
        let _m = m > 9 ? m : '0' + m
        //判断上个月天数
        var prevDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][prevMonth - 1]
        var calendar = []
        //这里塞入上个月末尾日期
        //day 日  active 是否选择 isNot 是不是这个月 formData 日期格式 isBg 是否加背景色（日期段筛选时使用）
        for (let i = 1; i <= fd; i++) {
          let prevDay = prevDays - fd + i
          let _prevDay = prevDay > 9 ? prevDay : '0' + prevDay

          calendar.push({
            day: prevDay,
            active: false,
            isNot: true,
            formData: prevYear + '-' + _prevMonth + '-' + _prevDay,
          })
        }
        //这里塞入正常这个月的日期
        let week = [
          global.i18n.t('星期日'),
          global.i18n.t('星期一'),
          global.i18n.t('星期二'),
          global.i18n.t('星期三'),
          global.i18n.t('星期四'),
          global.i18n.t('星期五'),
          global.i18n.t('星期六'),
        ]
        for (let i = 1; i <= allDays; i++) {
          let _i = i > 9 ? i : '0' + i
          calendar.push({
            day: i,
            active: false,
            isNot: false,
            formData: y + '-' + _m + '-' + _i,
            week: week[((i + fd - 1) % 7) % 7],
            year: y,
            month: m,
            allDays: allDays,
          })
        }
        //下个月是不是下一年
        let nextYear = y
        let nextMonth = m + 1
        if (m == 12) {
          nextYear = y + 1
          nextMonth = 1
        }
        let _nextMonth = nextMonth > 9 ? nextMonth : '0' + nextMonth
        //判断数组最后一排剩余几个位置塞入下个月日期
        let takedie = calendar.length % 7
        if (7 - takedie > 0 && 7 - takedie < 7) {
          for (let i = 1; i <= 7 - takedie; i++) {
            let _i = i > 9 ? i : '0' + i
            calendar.push({
              day: i,
              active: false,
              isNot: true,
              formData: nextYear + '-' + _nextMonth + '-' + _i,
            })
          }
        }
        // 默认当天时间加背景色
        if (this.mode == 'date') {
          // 在range模式时  start end不存在时走这个
          if (this.isDefaultDay || this.currentDay) {
            this.currentDayFun(calendar, this.currentDay, true)
          }
        }
        if (this.mode == 'range') {
          // 在range模式时  start end不存在时走这个
          if (this.isDefaultDay || this.currentDay) {
            if (!this.startTime || !this.endTime) {
              this.currentDayFun(calendar, this.currentDay, true)
            }
          }
          // 当开始时间与结束时间存在时
          if (this.startTime && this.endTime && this.startTime != this.endTime) {
            // 当没有选择过时 进行赋值操作  若是已经点击过 start end存在值 就不需要赋值了
            if (!this.start && !this.end) {
              this.start = this.startTime
              this.end = this.endTime
              // 判断开始结束值存入本地
              if (!uni.getStorageSync('startItem')) {
                let startItem = calendar.find((item) => item.formData == this.start)
                uni.setStorageSync('startItem', JSON.stringify(startItem))
              }
              if (!uni.getStorageSync('endItem')) {
                let endItem = calendar.find((item) => item.formData == this.end)
                uni.setStorageSync('endItem', JSON.stringify(endItem))
              }
            }
          }

          this.rangeDayFun(calendar, true)
        }
        if (this.mode == 'signin') {
          this.singinFun(calendar, this.signinData)
        }
        // 判断未来日期是否可选择
        if (!this.future) {
          let nowData = new Date().getTime()
          calendar.forEach((val) => {
            val.isFuture = false
            if (new Date(val.formData).getTime() > nowData) {
              val.isFuture = true
            }
          })
        }
        return calendar
      },
      /**
       * @description 高亮日期  签到模式
       */
      singinFun(calendar, data) {
        calendar.forEach((a) => {
          a.dayActive = false
          data.forEach((b) => {
            if (a.formData == b) {
              a.dayActive = true
            }
          })
        })
        this.calendar = calendar
        this.$forceUpdate()
      },
      /**
       * @description 单个日期是否高亮
       * @param {Array} calendar 日历数组
       * @param {String} day 选择的日期
       * @param {Boolen} isInit 初始化触发change时间参数
       */
      currentDayFun(calendar, day, isInit = false) {
        let that = this
        calendar.forEach((item) => {
          item.dayActive = false
          if (Math.floor(new Date(item.formData).getTime() / 1000) == Math.floor(new Date(day).getTime() / 1000)) {
            item.dayActive = true
            if (this.mode == 'date') {
              that.$emit('change', {
                day: item.day,
                time: item.formData,
                week: item.week,
                year: item.year,
                month: item.month,
                allDays: item.allDays,
                isInit: isInit,
              })
            }
          }
        })
        this.calendar = calendar
        this.$forceUpdate()
      },
      /**
       * @description 日期范围选择
       */
      rangeDayFun(calendar, isInit = false) {
        // 判断 start  end  和中间的时间
        calendar.forEach((val) => {
          val.start = false
          val.end = false
          val.active = false
          val.dayActive = false

          // 开始时间
          if (new Date(val.formData).getTime() == new Date(this.start).getTime()) {
            val.start = true
          }
          // 结束时间
          if (new Date(val.formData).getTime() == new Date(this.end).getTime()) {
            val.end = true
          }
          // 当开始结束时间选择完毕   开始时间大于结束时间时进行翻转
          if (this.start && this.end && new Date(this.start).getTime() > new Date(this.end).getTime()) {
            let start = this.start
            this.start = this.end
            this.end = start
          }
          // 开始结束中间时间段
          if (new Date(val.formData).getTime() > new Date(this.start).getTime() && new Date(val.formData)
            .getTime() < new Date(this.end).getTime()) {
            val.active = true
          }
        })

        if (this.start && this.end && this.isEmit) {
          let startItem = JSON.parse(uni.getStorageSync('startItem'))
          let endItem = JSON.parse(uni.getStorageSync('endItem'))
          this.$emit('change', {
            start: this.start,
            startYear: startItem.year,
            startWeek: startItem.week,
            startMonth: startItem.month,
            startDay: startItem.day,
            end: this.end,
            endYear: endItem.year,
            endWeek: endItem.week,
            endMonth: endItem.month,
            endDay: endItem.day,
            isInit: isInit,
          })
          this.isEmit = false
        }
        this.calendar = calendar
        this.$forceUpdate()
      },
      /**
       * @description 点击日期
       * @param {Object} item 入参 所点击日期信息
       */
      changeCalendar(item, index) {
        // 签到模式纯展示模式
        if (this.mode == 'signin') return
        // 未来日期 上下月日期不可选择
        if (item.isFuture || item.isNot) return
        if (this.mode == 'date') {
          if (this.currentDay == item.formData) return
          // 选择日期记录
          this.currentDay = item.formData
          this.currentDayFun(this.calendar, item.formData)
        }
        if (this.mode == 'range') {
          // 当已经选择了开始结束时间时  再次点击时先清空之前的时间
          if (this.start && this.end) {
            this.start = ''
            this.end = ''
            this.isEmit = true
            uni.removeStorageSync('startItem')
            uni.removeStorageSync('endItem')
          }
          // 默认先赋值开始时间
          if (!this.start && !this.end) {
            this.start = item.formData
            uni.setStorageSync('startItem', JSON.stringify(item))
          } else if (this.start && !this.end) {
            // 当第二次选择时间和第一次相同时  不再触发赋值
            // if (this.start == item.formData) return
            this.end = item.formData
            uni.setStorageSync('endItem', JSON.stringify(item))
          }

          this.rangeDayFun(this.calendar)
        }
      },
      /**
       * @description 上年今月
       */
      prevYear() {
        let currentDate = this.currentDate
        currentDate = [currentDate[0] - 1, currentDate[1]]
        this.currentDate = currentDate
        this.calendar = this.getCalendar(currentDate[0], currentDate[1])
      },
      /**
       * @description 下年今月
       */
      nextYear() {
        let currentDate = this.currentDate
        currentDate = [Number(currentDate[0]) + 1, Number(currentDate[1])]
        this.currentDate = currentDate
        this.calendar = this.getCalendar(currentDate[0], currentDate[1])
      },
      /**
       * @description 上月
       */
      prevMonth() {
        let currentDate = this.currentDate
        if (currentDate[1] - 1 == 0) {
          currentDate = [currentDate[0] - 1, 12]
        } else {
          currentDate = [currentDate[0], currentDate[1] - 1]
        }
        this.currentDate = currentDate
        this.calendar = this.getCalendar(currentDate[0], currentDate[1])
      },
      /**
       * @description 下月
       */
      nextMonth() {
        let currentDate = this.currentDate
        if (currentDate[1] == 12) {
          currentDate = [Number(currentDate[0]) + 1, 1]
        } else {
          currentDate = [Number(currentDate[0]), Number(currentDate[1]) + 1]
        }
        this.currentDate = currentDate
        this.calendar = this.getCalendar(currentDate[0], currentDate[1])
      },
    },
  }
</script>

<style scoped lang="scss">
  @import './css/iconfont.css';

  .fu-flex {
    display: flex;
  }

  .fu-justify-center {
    justify-content: center;
  }

  .fu-justify-between {
    justify-content: space-between;
  }

  .fu-align-center {
    align-items: center;
  }

  .fu-text-32 {
    font-size: 32rpx;
  }

  .fu-text-333 {
    color: #333333;
  }

  .fu-text-center {
    text-align: center;
  }

  .fu-margin {
    margin: 10rpx 0;
  }

  .fu-height-72 {
    height: 72rpx;
    line-height: 72rpx;
  }

  .fu-flex-wrap {
    flex-wrap: wrap;
  }

  // 日历选择 start
  .fu-calendar-data {
    .fu-title {
      padding: 0 10rpx;
    }

    .fu-arrow {
      height: 100%;

      // padding: 0 10rpx;
      text {
        font-size: 36rpx;
      }

      &.fu-left {
        transform: rotate(180deg);
        padding-left: 10rpx;
      }

      &.fu-right {
        padding-left: 10rpx;
      }

      &.single {
        text {
          font-size: 32rpx;
        }
      }
    }
  }

  // 日历选择 end
  // 星期 start
  .fu-week {
    .fu-week-item {
      width: calc(100% / 7);
    }
  }

  // 星期 end
  // 日历 start
  .fu-calendar {
    .fu-calendar-item {
      width: calc(100% / 7);
      padding-bottom: calc(100% / 7);
      position: relative;
      overflow: hidden;

      .fu-calendar-num {
        position: absolute;
        left: 0;
        top: 0;
        width: 100%;
        height: 100%;

        &.start {
          border-radius: 16rpx 0 0 16rpx;
        }

        &.end {
          border-radius: 0 16rpx 16rpx 0;
        }

        .fu-calendar-text {
          position: absolute;
          bottom: 4rpx;
          left: 0;
          width: 100%;
          font-size: 20rpx;
          color: #ffffff;
          text-align: center;
        }

        &.range {
          position: absolute;
          width: 100%;
          height: 90%;
          top: 50%;
          transform: translateY(-50%);
        }

        &.signin {
          position: absolute;
          width: 60%;
          height: 60%;
          left: 50%;
          top: 50%;
          transform: translate(-50%, -50%);
          border-radius: 50%;
        }
      }

      &.isNot {
        color: #999;
      }

      &.isFuture {
        color: #666;
      }
    }
  }

  // 日历 end
</style>
